/**
 * Author: Yuan Li
 * Titile: lab6 for the 15123 summer
 */

#include <stdio.h>
#include <stdlib.h>

/* my own macros */
#define true 1
#define false 0

#define DEBUG true
/*******************/

int main(int argc, char **argv){
	FILE *inFile,*outFile;
	short rdin1;
	short rdin2;
	short word;
	char byte;
	int retVal;
	short mask;
	short tmp;


	/* Check the argc before use argv */
	if (argc < 3){
		printf("You have to input the source file and dstination file names\n");
		exit(EXIT_FAILURE);
	}
	
	/* Open the source file and distination file*/
	inFile = fopen(argv[1],"rt");
	if (inFile == NULL){
		printf("fail to open the source file\n");
		exit(EXIT_FAILURE);
	}

	outFile = fopen(argv[2], "wb");
	if (outFile == NULL){
		printf("fail to open the dstination file\n");
		exit(EXIT_FAILURE);
	}

	/* Read from source file, then write to the dstination file (encode)*/
	rdin1 = 0;
	rdin2 = 0;
	word = 0;
	byte = 0;
	retVal = 0;
	mask = 0;
	while (true){
		/* Generally, we will continuely read numbers then pact them into a word+byte format */
		retVal = fscanf(inFile, "%hi", &rdin1);
		/* reach the end of file */
		if (retVal != 1){
			break;
		}
		retVal = fscanf(inFile, "%hi", &rdin2);
		/* Handle the odd numbers of integer condition, */
		if (retVal != 1){
			/* do not need encode */
			word = rdin1;
			retVal = fwrite(&word,sizeof(word),1,outFile);
			/* handle write exception */
			if (retVal != 1){
				printf("fail to write to the dst file\n");
				exit(EXIT_FAILURE);
			}
			break;
		}
		/* Succefully read 2 integers, do the encoding*/
		else{
			word = rdin1 << 4;
			/* make a mask to get the first 4bits from rdin2 */
			mask = (short) 0x8000;
			/* mask is signed short, so we got arithmatic shift
			 * in order to get 0xfff0, we have to shift (15-4)=11bits
			 */
			mask = mask >> 11;
			/* got 0x0x000f*/
			mask = ~mask;
			/* got the first 4 bits of rdin2 at right most of mask */
			mask = mask & (rdin2>>8);
			/* put the first 4 bits of rdin2 to the right most of word */
			word = word | mask;
			byte = (char) rdin2;

			/* write word and byte respectively */
			retVal = fwrite(&word,sizeof(word),1,outFile);
			if (retVal != 1){
				printf("fail to write to the dst file\n");
				exit(EXIT_FAILURE);
			}
			retVal = fwrite(&byte,sizeof(byte),1,outFile);
			if (retVal != 1){
				printf("fail to write to the dst file\n");
				exit(EXIT_FAILURE);
			}
		}
	}

	/* Finish encoding, then close the source and dst file */
	fclose(inFile);
	fclose(outFile);

	/* Decode the file */
	retVal = 0;
	word = 0;
	byte = 0;
	rdin1 = 0;
	rdin2 = 0;
	mask = 0;
	tmp = 0;
	inFile = fopen(argv[2],"rb");
	if (inFile == NULL){
		printf("fail to read from the %s\n", argv[2]);
		exit(EXIT_FAILURE);
	}

	/* read a word and byte then decode them */
	while (true){
		retVal = fread(&word,sizeof(word),1,inFile);
		if (retVal != 1){
			/* reach the end of the file */
			if (feof(inFile)){
				break;
			}
			printf("fail to read from %s\n", argv[2]);
			exit(EXIT_FAILURE);
		}
		
		retVal = fread(&byte,sizeof(byte),1,inFile);
		/* handle odd number of integer case */
		if (retVal != 1){
			printf("%hi\n",word);
			break;
		}
		/* decode 2 integers */
		else{
			/* (force a logic right shift) we need arithmatic shift */
			rdin1 = (((short) word) >> 4);
			/* make a mask to get the first 4bits from rdin2 */
			mask = (short) 0x8000;
			/* mask is signed short, so we got arithmatic shift
			 * in order to get 0xfff0, we have to shift (15-4)=11bits
			 */
			mask = mask >> 11;
			/* got 0x0x000f*/
			mask = ~mask;
			/* got the first 4bits of rdin2 (0x000X)*/
			tmp = word & mask;
			/* cast byte, be care about signed bit extension */
			rdin2 = (short) byte;
			/* get mask 0x00ff */
			mask = ~(((short)0x8000) >> 7);
			/* clear high 8 of rdin2 */
			rdin2 = mask & rdin2;
			/* put this bits into right position of rdin2 (0x0X00), done*/
			rdin2 = (short) (rdin2 | (tmp<<8));
			/* restore signed bits */
			rdin2 = ((short) (rdin2 << 4)) >> 4;
			
			/* print out rdin1 and rdin2 */
			printf("%hi\n", rdin1);
			printf("%hi\n", rdin2);
		}
	}
	fclose(inFile);

	return EXIT_FAILURE;
}
